package cc.rboot.lock.aspect;

import cc.rboot._core.helper.AnnotationHelper;
import cc.rboot._core.helper.MethodHelper;
import cc.rboot.lock.holder.LockHolder;
import cc.rboot.lock.annotation.Lockable;
import cc.rboot.lock.core.Locker;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.Objects;

@Aspect
@Order(Ordered.HIGHEST_PRECEDENCE)
public class LockableAspect {

    @Resource
    private LockHolder lockHelper;

    @Pointcut("@annotation(cc.rboot.lock.annotation.Lockable)")
    public void pointCut() {
    }

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        Method targetMethod = MethodHelper.get(joinPoint);

        Lockable lockable = AnnotationHelper.get(joinPoint, Lockable.class);

        // 如果添加了Lockable这个注解，需要添加分布式锁
        String subject = lockable.subject();
        String key = lockable.key();
        boolean usePrefix = lockable.usePrefix();
        if(Objects.isNull(key)) {
            try (Locker lock = lockHelper.lock(subject, usePrefix)) {
                return joinPoint.proceed();
            }
        }else {
            LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
            String[] paraNameArr = u.getParameterNames(targetMethod);

            //使用SPEL进行key的解析
            ExpressionParser parser = new SpelExpressionParser();
            //SPEL上下文
            StandardEvaluationContext context = new StandardEvaluationContext();
            //把方法参数放入SPEL上下文中
            for(int i=0;i<paraNameArr.length;i++){
                context.setVariable(paraNameArr[i], joinPoint.getArgs()[i]);
            }
            String secondKey = parser.parseExpression(key).getValue(context, String.class);

            try (Locker lock = lockHelper.lock(subject, secondKey, usePrefix)) {
                return joinPoint.proceed();
            }
        }

    }

}
